package com.feng.concurrency.cooperation;

import static net.mindview.util.Print.print;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;

import com.feng.concurrency.threading.LiftOff;

/**
 * @author fengsy
 * @date 5/13/21
 * @Description
 */
public class TestBlockingQueues {
    public static void main(String[] args) {
        test("LinkedBlockingQueue", // Unlimited size
            new LinkedBlockingQueue<LiftOff>());
        test("ArrayBlockingQueue", // Fixed size
            new ArrayBlockingQueue<LiftOff>(3));
        test("SynchronousQueue", // Size of 1
            new SynchronousQueue<LiftOff>());
    }

    static void getkey() {
        try {
            // Compensate for Windows/Linux difference in the
            // length of the result produced by the Enter key:
            new BufferedReader(new InputStreamReader(System.in)).readLine();
        } catch (java.io.IOException e) {
            throw new RuntimeException(e);
        }
    }

    static void getkey(String message) {
        print(message);
        getkey();
    }

    static void test(String msg, BlockingQueue<LiftOff> queue) {
        print(msg);
        LiftOffRunner runner = new LiftOffRunner(queue);
        Thread t = new Thread(runner);
        t.start();
        for (int i = 0; i < 5; i++) {
            runner.add(new LiftOff(5));
        }
        getkey("Press ‘Enter’ (" + msg + ")");
        t.interrupt();
        print("Finished " + msg + " test");
    }

    /**
     * LiftOffRunner can ignore synchronization issues because they are solved by the BlockingQueue.
     */
    static class LiftOffRunner implements Runnable {
        private BlockingQueue<LiftOff> rockets;

        public LiftOffRunner(BlockingQueue<LiftOff> queue) {
            rockets = queue;
        }

        public void add(LiftOff lo) {
            try {
                rockets.put(lo);
            } catch (InterruptedException e) {
                print("Interrupted during put()");
            }
        }

        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    LiftOff rocket = rockets.take();
                    rocket.run(); // Use this thread
                }
            } catch (InterruptedException e) {
                print("Waking from take()");
            }
            print("Exiting LiftOffRunner");
        }
    }
}
